#include "../include/unrealcontrollerbot.h"


using namespace Gtk;
using namespace std;

UnrealControllerBot::UnrealControllerBot(RefPtr<SocketClient>& connectionSource, ustring addr, unsigned port):UnrealBot(connectionSource, addr, port)
{
	mapsKnown = false;
	botstream = 0;

	logger.unparent();
	infos = new HPaned();
	ScrolledWindow* logscroll = new ScrolledWindow();
	logscroll->add(logger);
	((HPaned*)infos)->pack1(*logscroll, true, false);

	((HPaned*)infos)->pack2(botsBox, true, false);
	botsBox.pack_start(controllerToolbar, false, false, 0);

	controllerToolbar.append(createBotButton);
	createBotButton.set_stock_id(Gtk::Stock::NEW);
	createBotButton.set_label("new native bot");
	createBotButton.signal_toggled().connect(sigc::mem_fun(*this, &UnrealControllerBot::onToggle));
	controllerToolbar.append(setSpeedButton);
	setSpeedButton.set_stock_id(Stock::MEDIA_FORWARD);
	setSpeedButton.set_label("set gamespeed");

	controllerToolbar.append(setMapButton);
	setMapButton.set_stock_id(Stock::INDEX);
	setMapButton.set_label("select Map");
	setMapButton.signal_toggled().connect(sigc::mem_fun(*this, &UnrealControllerBot::onToggle));

	botsBox.pack_start(addBotBar, Gtk::PACK_SHRINK);
	botsBox.pack_start(setMapBar, Gtk::PACK_SHRINK);
	botsBox.pack_start(botsScroll, Gtk::PACK_EXPAND_WIDGET);


//addbotbar
	Gtk::Container* addBotBarContainer =
	    dynamic_cast<Gtk::Container*>(addBotBar.get_content_area());
	HBox* addBotBarHBox = new HBox;
	addBotBarContainer->add(*addBotBarHBox);

	addBotOk.set_label("Ok");
	addBotOk.signal_clicked().connect(sigc::mem_fun(*this, &UnrealControllerBot::onAddNativeBot));

	addBotBarHBox->pack_start(addBotName , Gtk::PACK_EXPAND_WIDGET);
	addBotBarHBox->pack_start(addBotColor, Gtk::PACK_SHRINK);
	addBotColor.set_label("   ");

	addBotBarHBox->pack_start(addBotSkill, Gtk::PACK_SHRINK);
	addBotBarHBox->pack_start(addBotOk   , Gtk::PACK_SHRINK);
	addBotBar.show_all_children();
	addBotBar.set_no_show_all();


///setMapBar
	botsBox.pack_start(botsScroll, Gtk::PACK_EXPAND_WIDGET);
	botsScroll.add(botsList);
	Gtk::Container* setMapBarContainer =
	    dynamic_cast<Gtk::Container*>(setMapBar.get_content_area());
	HBox* setMapBarHBox = new HBox;
	setMapBarContainer->add(*setMapBarHBox);

	setMapBarHBox->pack_end(setMapOk, PACK_SHRINK);
	setMapOk.set_label("Set Map");
	setMapOk.signal_clicked().connect(sigc::mem_fun(*this, &UnrealControllerBot::onSetMap));
	setMapBarHBox->pack_end(setMapSelect, PACK_EXPAND_WIDGET);
	setMapBar.show_all_children();
	setMapBar.set_no_show_all();
	//ctor
	infos->show_all();

}

UnrealBotInfo* UnrealControllerBot::getBot(string id)
{
	for(vector<UnrealBotInfo*>::iterator iter = bots.begin(); iter<bots.end(); iter++)
		if((*iter)->getSubject() && (*iter)->getSubject()->id==id)
			return (*iter);
		else if((*iter)->getSubject()==0)
			logger << "subjectNull" ;
	return 0;
}
UnrealBotInfo* UnrealControllerBot::getBot(UnrealNativeBot* id)
{
	for(vector<UnrealBotInfo*>::iterator iter = bots.begin(); iter<bots.end(); iter++)
		if((*iter)->getSubject()==id)
			return (*iter);
	return 0;
}
UnrealBotInfo* UnrealControllerBot::getBot(unsigned i)
{
	if (i>=bots.size()) return 0;
	return bots[i];
}


void UnrealControllerBot::processInput()
{
	UnrealBot::processInput();// do nothing if the the UnrealBot::processinput has handled the message
	Mutex::Lock lock(mutex);
	//process the standard input

	int matchval = botstream->matchVal();
	switch (matchval) {
	case 'IOJ': { //JOIN
		UnrealNativeBot newBot(map);
		for (int i=0; i<botstream->fieldCount(); i++) {
			if((*botstream)[i].first=="Id")
				newBot.id   = (*botstream)[i].second;
			else if((*botstream)[i].first=="Name")
				newBot.name = (*botstream)[i].second;
		}
	}
	break;
	case 'AMS'://SMAP
		handleSMap();
		break;

	case 'RLP': { //plr
//        PLR {Id CTF-UG-Chrome.GBxBot1} {Name piet} {Team 0} {Location 487.12,8827.83,-545.36} {Rotation 60157,22239,0} {Velocity -136.11,216.39,-875.19} {Weapon XWeapons.Translauncher}
		string id, nm;
		int team;
		string place;
		string weapon;
		for (int i=0; i<botstream->fieldCount(); i++) {
			if((*botstream)[i].first=="Id")
				id = (*botstream)[i].second;
			else if((*botstream)[i].first=="Name")
				nm = (*botstream)[i].second;
			else if((*botstream)[i].first=="Team")
				team = atoi((*botstream)[i].second.c_str());
			else if((*botstream)[i].first=="Location")
				place =(*botstream)[i].second;
			else if((*botstream)[i].first=="Weapon")
				weapon = (*botstream)[i].second;
		}
		UnrealNativeBot* newB = getBot(id)->getSubject();
		if (newB) {
			((UnrealPlace*) newB->place)->parse(place);//just update it if it is already known
			newB->name=nm;
			newB->weapon=weapon;
		} else {
			//add it if it's not known yet
			newB  = new UnrealNativeBot(&handler->map, nm, id, team, new UnrealPlace(place), weapon);
			addWatchBot(newB);
		}
		if(handler->map.finalized)
			handler->mapDraw->queue_draw();
	}
	break;
	case 'DNE'://END
		botstream->flushCommands();
		break;
	case 'NIF'://FIN
//		handler->reconnectAll();
		logger << "disconnected";
		state=awaiting_connect;
		connection->close();
		break;
	case 'GLF'://flg (flag location)
	{
		string team;
		UnrealPlace flagPlace;
		for (int i=0; i<botstream->fieldCount(); i++) {
			if((*botstream)[i].first=="Team"){
				team = (*botstream)[i].second;
			}
			else if((*botstream)[i].first=="Location"){
				flagPlace.parse((*botstream)[i].second);
			}
		}
	}
	break;
	case 'SET'://TES (team status)
	{
		string team;
		int score;
		for (int i=0; i<botstream->fieldCount(); i++) {
			if((*botstream)[i].first=="Team"){
				team = (*botstream)[i].second;
			}
			else if((*botstream)[i].first=="Score"){
				score = atoi((*botstream)[i].second.c_str());
			}
		}
	}
	case 'YEK': //KEYEVENT: we don't care what keys users press( bots don't press keys.
	case 'BEG': // beginning of a round.
	case 'END':
		return;
	default:
		logger.log("unknown command " + botstream->currentMessage());
		FORALL(pair<string COMMA string>, it, botstream->fields)
			logger.log("\t" + (*it).first + "=> " + (*it).second);
	}
}

void UnrealControllerBot::handleSMap()
{
	while (1) {
		botstream->nextMessage();
		int matchval = botstream->matchVal();
		if(matchval == 'AMI') { //IMAP
			for(int i=0; i<botstream->fieldCount(); i++)
				if(botstream->fields[i].first=="Name")
					maps.insert(botstream->fields[i].second);

		} else if(matchval == 'AME') { // EMAP
			mapsKnown=true;
			break;
		} else {
			logger.logRed("wrong message received while reading maps:" + botstream->currentMessage());
			break;
		}
	}
	set<string>::iterator iter;
	setMapSelect.hide();
	for(iter = maps.begin(); iter != maps.end(); iter++) {
		logger.log("found map:" + *iter);
		setMapSelect.append_text( *iter);
	}
	setMapSelect.show_all();
}


UnrealControllerBot::~UnrealControllerBot()
{
	//dtort
}
bool UnrealControllerBot::connect(ustring addr, unsigned port){
    // We connect to the server
			adres = addr;
			port  = port;
			if ( connection && !connection->is_closed() ){
				cout << "connection exists" << endl;
				logger.logRed("connection already exists");
				return false;
			}
            try{
                connection =
                    socketSource->connect_to_host(adres, port);
            }
            catch(Gio::Error& e){
                logger.logRed(ustring("connection Failed: ") + e.what());
                return false;
            }
            catch(Glib::Error& e){
                logger.logRed(ustring("connection Failed: ") + e.what());

                return false;
            }

            catch(...){
                logger.logRed(ustring("connection Failed"));
                return false;
            }

		if(botstream)
			delete botstream;
		botstream = new UnrealStream(
					DataInputStream::create(connection->get_input_stream()) ,
                    DataOutputStream::create(connection->get_output_stream())
                );

    logger.log("connecting...", true);
 // We read the response of the server
	string buffer ="";
	try{
		buffer = botstream->nextMessage();
	}
	catch(Glib::Error& e){
		logger.logRed(ustring("connection Failed: ") + e.what());
		connection->close();
		return false;
	}
//cout << "received: " << buffer << std::endl;
    if (buffer == "HELLO_CONTROL_SERVER"){
        state= connected;
        logger.logGreen( "Connected; hello received" );
        botstream->putCommand("READY");
		if(state & awaiting_connect)
			state = running;
		else
			state = connected;
        return  true;
    }else{
        logger.logRed("failed to connect properly.\n received: ");
        logger.logRed(buffer);
//               connection->close();
        return false;
    }
}



bool UnrealControllerBot::addWatchBot(UnrealNativeBot* watchedBot)
{
	if(getBot(watchedBot)){
		return false; // already added;
	}
	logger.log(ustring("watching bot: ") + ustring(watchedBot->name));

	UnrealBotInfo* newIfo = new UnrealBotInfo(watchedBot);
	bots.push_back(newIfo);
	botsList.pack_end(*newIfo);
	manage(newIfo);
	newIfo->show();
	botsList.show_all();
	handler->mapDraw->addBotToDraw(watchedBot);
	return true;
}

void UnrealControllerBot::onAddNativeBot()
{
	AddBotCommand* cmd = new AddBotCommand;
	cmd->name = addBotName.get_text();
	cmd->startLocation = handler->gameInfos.BlueBaseLocation->toString();
	cmd->skill = addBotSkill.get_value();
	cmd->team  = addBotColor.getTeam();
	botstream->queueCommand( cmd );
}

void UnrealControllerBot::onToggle()
{
	if(createBotButton.property_active())
		addBotBar.show();
	else
		addBotBar.hide();
	if(setMapButton.property_active()) {
		setMapBar.show();
		if(!mapsKnown)
			botstream->queueCommand(new GetMapsCommand);
		logger << "getting maps";
	} else {
		setMapBar.hide();
	}
}

void UnrealControllerBot::onSetMap()
{
	Mutex::Lock lock(mutex);
	string newMapName = setMapSelect.get_active_text();
	if (maps.find(newMapName)==maps.end())
		return;
	botstream->queueCommand(new ChangeMapCommand(newMapName));
	handler->map.invalidate();
	delete handler->mapDraw;
	handler->mapDraw=0;
}
